#include "../quickjs.c"

/* Runtime创建
 * 1. 内存分配方法
 * 2. 每个context共享一份atoms
 * 3. 为内置的对象生成class */
struct JSRuntime;
JSRuntime *JS_NewRuntime();
/* 为每种对象生成class */
struct JSClass;
int JS_NewClass1(JSRuntime *rt, JSClassID class_id,
                 const JSClassDef *class_def, JSAtom name);
/* Context创建
 * 1. 初始化内置构造函数的原型对象
 * 2. 向全局对象global_obj添加标准库对象（Object、Boolean、String） */
struct JSContext;
JSContext *JS_NewContext(JSRuntime *rt);

/* 编译&执行入口 
 * flags:
 *   JS_EVAL_TYPE_GLOBAL   (0 << 0) global code (default) 
 *   JS_EVAL_TYPE_MODULE   (1 << 0) module code
 *   JS_EVAL_FLAG_COMPILE_ONLY (1 << 5) 只编译不执行 */
JSValue JS_Eval(JSContext *ctx, const char *input, size_t input_len,
                const char *filename, int eval_flags);
JSValue __JS_EvalInternal(JSContext *ctx, JSValueConst this_obj,
                          const char *input, size_t input_len,
                          const char *filename, int flags, int scope_idx);

struct JSFunctionDef;
JSFunctionDef *js_new_function_def(JSContext *ctx,
                                   JSFunctionDef *parent,
                                   BOOL is_eval,
                                   BOOL is_func_expr,
                                   const char *filename, int line_num);


/* 解析 */
int js_parse_program(JSParseState *s);
/* 解析'use strict'、'use strip'、'use math'命令 */
int js_parse_directives(JSParseState *s);
/* 解析源码中的一个元素（语句） */
int js_parse_source_element(JSParseState *s);
int js_parse_function_decl(JSParseState *s,
                           JSParseFunctionEnum func_type,
                           JSFunctionKindEnum func_kind,
                           JSAtom func_name,
                           const uint8_t *ptr,
                           int function_line_num);
/**
 * 解析语句或者声明
 * @param s 
 * @param decl_mask 
 *   block {}
 *   return
 *   try/throw
 *   let/const/var
 *   if/while/do/for/switch
 *   break/continue
 *   with
 *   function/class
 *   ident
 *   debugger
 */
int js_parse_statement_or_decl(JSParseState *s, int decl_mask);
int js_parse_var(JSParseState *s, BOOL in_accepted, int tok, BOOL export_flag);
int js_parse_class(JSParseState *s, BOOL is_class_expr, JSParseExportEnum export_flag);
int js_parse_expr(JSParseState *s);
int js_parse_assign_expr(JSParseState *s, BOOL in_accepted);
int js_parse_cond_expr(JSParseState *s, BOOL in_accepted);
int js_parse_coalesce_expr(JSParseState *s, BOOL in_accepted);
int js_parse_logical_and_or(JSParseState *s, int op, BOOL in_accepted);
int js_parse_expr_binary(JSParseState *s, int level, BOOL in_accepted);
int js_parse_unary(JSParseState *s, int exponentiation_flag);
int js_parse_postfix_expr(JSParseState *s, BOOL accept_lparen);

/* 定义变量 */
int add_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name);
int define_var(JSParseState *s, JSFunctionDef *fd, JSAtom name, JSVarDefEnum var_def_type);
int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name);
int add_scope_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name, JSVarKindEnum var_kind);
/* 对于global/module层级的代码，var声明的变量和最外层的let/const声明的变量都需要放在这里
 * 将var和最外层的let/const变量放在一起是因为这两种变量不能重复定义 */
JSHoistedDef *add_hoisted_def(JSContext *ctx,
                              JSFunctionDef *s, int cpool_idx,
                              JSAtom name, int var_idx, BOOL is_lexical);

/**
 * @return JSValue(JSFunctionBytecode)
 */
struct JSFunctionBytecode;
JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd);
int resolve_variables(JSContext *ctx, JSFunctionDef *s);
/* 初始化提升的变量和函数 */
void instantiate_hoisted_definitions(JSContext *ctx, JSFunctionDef *s, DynBuf *bc);
/* 确定作用域中的变量来源（本地、参数、自由变量、全局变量、with对象） */
int resolve_scope_var(JSContext *ctx, JSFunctionDef *s,
                      JSAtom var_name, int scope_level, int op,
                      DynBuf *bc, uint8_t *bc_buf,
                      LabelSlot *ls, int pos_next, int arg_valid);
int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name);
int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name);
int get_closure_var(JSContext *ctx, JSFunctionDef *s,
                    JSFunctionDef *fd, BOOL is_arg,
                    int var_idx, JSAtom var_name,
                    BOOL is_const, BOOL is_lexical,
                    JSVarKindEnum var_kind);
/* 添加this/new.target/this.active_func/home_object/this.active_func这些动态变量 */
int resolve_pseudo_var(JSContext *ctx, JSFunctionDef *s,
                       JSAtom var_name);
int resolve_labels(JSContext *ctx, JSFunctionDef *s);
int compute_stack_size(JSContext *ctx, JSFunctionDef *fd, int *pstack_size);

/**
 * 执行函数
 */
JSValue JS_EvalFunctionInternal(JSContext *ctx, JSValue fun_obj,
                                JSValueConst this_obj,
                                JSVarRef **var_refs, JSStackFrame *sf);
JSValue js_closure(JSContext *ctx, JSValue bfunc,
                   JSVarRef **cur_var_refs,
                   JSStackFrame *sf);
JSValue js_closure2(JSContext *ctx, JSValue func_obj,
                    JSFunctionBytecode *b,
                    JSVarRef **cur_var_refs,
                    JSStackFrame *sf);
JSVarRef *get_var_ref(JSContext *ctx, JSStackFrame *sf,
                      int var_idx, BOOL is_arg);
JSValue JS_CallInternal(JSContext *ctx, JSValueConst func_obj,
                        JSValueConst this_obj, JSValueConst new_target,
                        int argc, JSValue *argv, int flags);
/**
 * 根据class和原型生成对象
 * @param ctx 
 * @param proto_val 
 * @param class_id 
 *     JS_CLASS_OBJECT
 *     JS_CLASS_ARRAY
 *     JS_CLASS_ERROR
 *     JS_CLASS_NUMBER
 *     ...
 * @return 
 */
JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val,
                               JSClassID class_id);
/**
 * 添加或修改对象的属性
 * @param flags
 *    JS_PROP_HAS_SHIFT
 *    JS_PROP_HAS_CONFIGURABLE  属性描述属性可修改或者属性可删除
 *    JS_PROP_HAS_WRITABLE      属性值可修改
 *    JS_PROP_HAS_ENUMERABLE    属性可以枚举（通过Object.keys可以获取到）
 *    JS_PROP_HAS_GET           设置getter
 *    JS_PROP_HAS_SET           设置setter
 *    JS_PROP_HAS_VALUE         设置value
 */
int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
                      JSAtom prop, JSValueConst val,
                      JSValueConst getter, JSValueConst setter, int flags);
/**
 * 根据字符串，生成atom，atom是一个32为的数字
 * 其中最高位为1的atom表示数字，值为去除最高位后数字表示
 * @param ctx 
 * @param str 
 * @return 
 */
JSAtom JS_NewAtom(JSContext *ctx, const char *str);

JSValue JS_NewObjectProtoClass(JSContext *ctx, JSValueConst proto_val, JSClassID class_id);
JSShape *js_new_shape2(JSContext *ctx, JSObject *proto,
                       int hash_size, int prop_size);
JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID class_id);

int JS_DefineProperty(JSContext *ctx, JSValueConst this_obj,
                      JSAtom prop, JSValueConst val,
                      JSValueConst getter, JSValueConst setter, int flags);
JSShapeProperty *find_own_property(JSProperty **ppr, JSObject *p, JSAtom atom);
int JS_CreateProperty(JSContext *ctx, JSObject *p,
                      JSAtom prop, JSValueConst val,
                      JSValueConst getter, JSValueConst setter,
                      int flags);
JSProperty *add_property(JSContext *ctx, JSObject *p, JSAtom prop, int prop_flags);
int add_shape_property(JSContext *ctx, JSShape **psh,
                       JSObject *p, JSAtom atom, int prop_flags);

JSValue JS_GetPropertyInternal(JSContext *ctx, JSValueConst obj,
                               JSAtom prop, JSValueConst this_obj,
                               BOOL throw_ref_error);
JSShapeProperty *find_own_property(JSProperty **ppr, JSObject *p, JSAtom atom);

int JS_EnqueueJob(JSContext *ctx, JSJobFunc *job_func,
                  int argc, JSValueConst *argv);
JSValue js_os_setTimeout(JSContext *ctx, JSValueConst this_val,
                         int argc, JSValueConst *argv);

void js_std_loop(JSContext *ctx);
int JS_ExecutePendingJob(JSRuntime *rt, JSContext **pctx);
int js_os_poll(JSContext *ctx);

/******** GC ********/
JSGCObjectHeader h;
void js_trigger_gc(JSRuntime *rt, size_t size);
void JS_RunGC(JSRuntime *rt);

void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, JSGCObjectTypeEnum type);
void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp);

void __JS_FreeValueRT(JSRuntime *rt, JSValue v);
void free_zero_refcount(JSRuntime *rt);
void free_object(JSRuntime *rt, JSObject *p);
void free_function_bytecode(JSRuntime *rt, JSFunctionBytecode *b);

/* debug */
char *JS_AtomGetStrRT(JSRuntime *rt, char *buf, int buf_size, JSAtom atom);
JSValue JS_AtomToString(JSContext *ctx, JSAtom atom);
char *JS_ToCStringLen(JSContext *ctx, size_t *plen, JSValueConst val1);
//JS_ToCStringLen(ctx, NULL, JS_AtomToString(ctx, atom));
